package br.com.orion.sga.service;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import br.com.orion.sga.model.dao.AlunoDao;
import br.com.orion.sga.model.entities.PeriodoLiberacao;
import br.com.orion.sga.to.MonthPaymentReport;

@Service
public class GeneratePaymentReport {

	/*Esta lista é para salvar todos os dias encontrados para depois estabelecermos
	 *uma relação simétrica entre os meses ao serem plotados no gráfico*/
	private ArrayList<Integer> allDaysFound = new ArrayList<Integer>();
	
	@Autowired
	private AlunoDao alunoDao;
	
	public LinkedHashMap<String,MonthPaymentReport> generatePaymentReport(Date initialDate, Date endDate){
		LinkedHashMap<String,MonthPaymentReport> reportValues = new LinkedHashMap <String,MonthPaymentReport>();
		String monthAndYear = null;
		int day = 0;
		MonthPaymentReport monthPaymentReport = null;
		List<PeriodoLiberacao> alunos = alunoDao.getAlunosForReportPayment(initialDate, endDate);
		for(PeriodoLiberacao p : alunos){
			monthAndYear = getMonthAndYearFromDate(p.getPeriodo());
			monthPaymentReport = getMonthPaymentReport(monthAndYear,reportValues);
			day = getDayFromDate(p.getPeriodo());
			
			/*Aqui estamos adicionando somente a previsão de valores com base na data
			 * de bloqueio do aluno (Até Quando)*/
			addValueToDay(day,p.getAluno().getValorMensalidade(),monthPaymentReport);
			
			
			/*Aqui estamos adicionando as demais previsões com base no dia de pagamento, 
			 *os valores adicionados aqui serão aqueles que estão previsto para vencer no
			 *futuro e que a data do vencimento esteja dentro do período escolhido no inínio
			 *do relatório*/
			adicionarMaisPrevisoesDePagamentos(p,reportValues,endDate);
			
			/*Isto é para manter a simetria entre os meses*/
			for(String key : reportValues.keySet()){
				MonthPaymentReport m = reportValues.get(key);
				for(Integer dayF : allDaysFound){
					if(!m.getPaymentDays().containsKey(dayF)){
						m.getPaymentDays().put(dayF, "-");
					}
				}
			}
		}
		return reportValues;
	}
	
	public void adicionarMaisPrevisoesDePagamentos(PeriodoLiberacao p,
		LinkedHashMap<String,MonthPaymentReport> reportValues,Date endDate ){
		int planoDePagamento = getPlanoDePagamentoAsInt(p.getAluno().getPlanoPagamento().getDescricao());
		String monthAndYear = null;
		MonthPaymentReport monthPaymentReport = null;
		int day = 0;
		
		Calendar cal = Calendar.getInstance();
		cal.setTime(p.getPeriodo());
		cal.set(Calendar.MONTH, cal.get(Calendar.MONTH) + planoDePagamento);
		
		while(cal.getTime().before(endDate) || cal.getTime().equals(endDate)){
			monthAndYear = getMonthAndYearFromDate(cal.getTime());
			monthPaymentReport = getMonthPaymentReport(monthAndYear,reportValues);
			day = p.getAluno().getVencimento().getDia();
			addValueToDay(day, p.getAluno().getValorMensalidade(), monthPaymentReport);
			cal.set(Calendar.MONTH, cal.get(Calendar.MONTH) + planoDePagamento);
		}
	}
	
	public MonthPaymentReport getMonthPaymentReport(String monthAndYear,
			LinkedHashMap<String,MonthPaymentReport> reportValues){
		MonthPaymentReport monthPaymentReport = reportValues.get(monthAndYear);
		if(monthPaymentReport == null){
			monthPaymentReport = new MonthPaymentReport(monthAndYear);
			reportValues.put(monthAndYear, monthPaymentReport);
		}
		return monthPaymentReport;
	}
	
	public int getPlanoDePagamentoAsInt(String planoDePagamento){
		switch (planoDePagamento) {
		case "Mensal":
			return 1;
		case "Trimestral":
			return 2;
		case "Semestral":
			return 3;
		case "Anual":
			return 12;
		}
		
		//Will never happen
		return 0;
	}
	
	public String getMonthAndYearFromDate(Date ateQuandoDate){
		String month = null;
		String year  = null;
		Calendar cal = Calendar.getInstance();
		cal.setTime(ateQuandoDate);
		
		month = cal.getDisplayName(Calendar.MONTH, Calendar.LONG, Locale.getDefault());
		year  = String.valueOf(cal.get(Calendar.YEAR));
		
		return abreviateMonth(month)+"/"+year;
	}
	
	public String abreviateMonth(String month){
		switch (month) {
			case "Janeiro" :
				return "JAN";
			case "Fevereiro":
				return "Fev";
			case "Março":
				return "Mar";
			case "Abril":
				return "Abr";
			case "Maio":
				return "Mai";
			case "Junho":
				return "Jun";
			case "Julho":
				return "Jul";
			case "Agosto":
				return "Ago";
			case "Setembro":
				return "Set";
			case "Outubro":
				return "Out";
			case "Novembro":
				return "Nov";
			case "Dezembro":
				return "Dez";
			case "January" :
				return "JAN";
			case "February":
				return "Fev";
			case "March":
				return "Mar";
			case "April":
				return "Abr";
			case "May":
				return "Mai";
			case "June":
				return "Jun";
			case "July":
				return "Jul";
			case "August":
				return "Ago";
			case "September":
				return "Set";
			case "October":
				return "Out";
			case "November":
				return "Nov";
			case "December":
				return "Dez";
	
			default:
				return null;
		}
	}
	
	public int getDayFromDate(Date ateQuandoDate){
		Calendar cal = Calendar.getInstance();
		cal.setTime(ateQuandoDate);
		return cal.get(Calendar.DAY_OF_MONTH);
	}
	
	public void addValueToDay(int day,float value, MonthPaymentReport monthPaymentReport){
		String valueOfDay = monthPaymentReport.getPaymentDays().get(day);
		float valueOfDayFloat = 0;
		
		/*Não existe valores para este dia neste mes*/
		if(valueOfDay == null || valueOfDay == "-"){
			monthPaymentReport.getPaymentDays().put(day, String.valueOf(value));
			/*É necessário adicionar os dias que são diferentes do padrão para manter
			 * a simetria no gráfico e na tabela posteriormente*/
			allDaysFound.add(day);
		}else{
			valueOfDayFloat = Float.parseFloat(valueOfDay);
			monthPaymentReport.getPaymentDays().put(day, String.valueOf(valueOfDayFloat+value));
		}
	}
}
